/*
 * 类名称:ApplyInfoServiceImpl.java
 * 包名称:com.lht.admin.service.impl
 *
 * 修改履历:
 *     日期                       修正者        主要内容
 *     2019-12-29 13:30:03        ZhengZhanWu     初版做成
 */
package com.lht.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lht.admin.constants.DataValidConstant;
import com.lht.admin.dao.ApplyInfoDao;
import com.lht.admin.entity.*;
import com.lht.admin.service.*;
import com.lht.admin.utils.ListUtil;
import com.lht.admin.utils.QueryPlus;
import com.lht.admin.utils.SerialNumberUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 借款数据信息表Service实现类
 *
 * @author ZhengZhanWu
 * @date 2019-12-29 13:30:03
 */
@Slf4j
@Service("applyInfoService")
public class ApplyInfoServiceImpl extends ServiceImpl<ApplyInfoDao, ApplyInfoEntity> implements ApplyInfoService {

    private ThreadLocal<Date> dateThreadLocal = new ThreadLocal<>();
    private ThreadLocal<SysUserEntity> operatorThreadLocal = new ThreadLocal<>();


    @Resource
    private PersonInfoService personInfoService;
    @Resource
    private CompanyInfoService companyInfoService;
    @Resource
    private AssetsCarInfoService assetsCarInfoService;
    @Resource
    private AssetsCreditInfoService assetsCreditInfoService;
    @Resource
    private AssetsHouseInfoService assetsHouseInfoService;
    @Resource
    private WorkCompanyInfoService workCompanyInfoService;
    @Resource
    private LoanInfoService loanInfoService;
    @Resource
    private TaxInfoService taxInfoService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private LoanApplyReplaceUserLogService loanApplyReplaceUserLogService;


    @Override
    public List<ApplyInfoEntity> queryAll(Map<String, Object> params) {
        return baseMapper.queryAll(params);
    }

    @Override
    public Page queryPage(Map<String, Object> params) {
        //排序
        Page<ApplyInfoEntity> page = new QueryPlus<ApplyInfoEntity>(params).getPage();
        List<ApplyInfoEntity> values = baseMapper.selectApplyInfoPage(page, params);

        if (null != values) {
            for (ApplyInfoEntity each : values) {
                SysUserEntity target = sysUserService.queryObject(each.getPromoUserId());
                each.setPromoUser(target);
            }
        }

        return page.setRecords(values);
    }


    @Override
    public ApplyInfoEntity queryById(Long id) {
        Map<String, Object> map = new HashMap<>(1);
        map.put("id", id);

        ApplyInfoEntity applyInfo = ListUtil.getFirstElement(queryAll(map));
        if (null != applyInfo) {
            loadAssetInfos(applyInfo, applyInfo.getApplyNo());
            loadLoanInfos(applyInfo, applyInfo.getApplyNo());
            loadTaxInfos(applyInfo, applyInfo.getApplyNo());
        }

        return applyInfo;
    }

    private void loadTaxInfos(ApplyInfoEntity applyInfo, String applyNo) {
        List<TaxInfoEntity> list = taxInfoService.list(buildApplyNoQueryWrapper(applyNo));
        applyInfo.setTaxInfoList(ListUtil.fixList(list));
    }

    private void loadLoanInfos(ApplyInfoEntity applyInfo, String applyNo) {
        List<LoanInfoEntity> list = loanInfoService.list(buildApplyNoQueryWrapper(applyNo));
        applyInfo.setLoanInfoList(ListUtil.fixList(list));
    }

    /**
     * 加载资产信息
     *
     * @param applyInfo applyInfo
     */
    private void loadAssetInfos(ApplyInfoEntity applyInfo, String applyNo) {

        AssetsCarInfoEntity carInfo = ListUtil.getFirstElement(assetsCarInfoService.list(buildApplyNoQueryWrapper(applyNo)));
        applyInfo.setAssetsCarInfo(carInfo);

        AssetsHouseInfoEntity houseInfo = ListUtil.getFirstElement(assetsHouseInfoService.list(buildApplyNoQueryWrapper(applyNo)));
        applyInfo.setAssetsHouseInfo(houseInfo);


        AssetsCreditInfoEntity creditInfo = ListUtil.getFirstElement(assetsCreditInfoService.list(buildApplyNoQueryWrapper(applyNo)));
        applyInfo.setAssetsCreditInfo(creditInfo);

        WorkCompanyInfoEntity workCompanyInfo = ListUtil.getFirstElement(workCompanyInfoService.list(buildApplyNoQueryWrapper(applyNo)));
        applyInfo.setWorkCompanyInfo(workCompanyInfo);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(SysUserEntity operator, ApplyInfoEntity applyInfo) {
        boolean result;
        //todo 生成申请编号
        String applyNo = SerialNumberUtils.generateNo(operator.getUserId(), SerialNumberUtils.TYPE_APPLY_NO);
        try {
            dateThreadLocal.set(new Date());
            operatorThreadLocal.set(operator);

            applyInfo.setApplyNo(applyNo);
            result = workoutSaveOrUpdate(applyInfo);

            log.info("Function[add],applyNo:{},result:{}", applyNo, result);
        } catch (Exception e) {
            log.error("Function[add],apply:" + applyNo, JSON.toJSONString(applyInfo));
            throw e;
        } finally {
            dateThreadLocal.remove();
            operatorThreadLocal.remove();
        }

        return result;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(ApplyInfoEntity applyInfo) {
        log.info("Function[update] info:{}", JSON.toJSONString(applyInfo));
        try {
            return workoutSaveOrUpdate(applyInfo);
        } catch (Exception e) {
            log.info("Function[update],info:" + applyInfo.getId(), e);
            return false;
        } finally {
            dateThreadLocal.remove();
        }
    }

    /**
     * 更新或者保存的整一个操作流程
     *
     * @param applyInfo ApplyInfoEntity
     * @return boolean
     */
    private boolean workoutSaveOrUpdate(ApplyInfoEntity applyInfo) {
        dateThreadLocal.set(new Date());

        dealWithPersonInfo(applyInfo);
        dealWithWorkCompanyInfo(applyInfo);
        dealWithCompanyInfo(applyInfo);
        dealWithAssetsInfo(applyInfo);
        dealWithTaxInfos(applyInfo);
        dealWithLoanInfos(applyInfo);

        return saveOrUpdate(applyInfo);
    }

    private void dealWithWorkCompanyInfo(ApplyInfoEntity applyInfo) {
        if (ApplyInfoEntity.ApplyTypeEnum.COMPANY.getValue().equals(applyInfo.getApplyType())) {
            return;
        }
        WorkCompanyInfoEntity workCompanyInfo = applyInfo.getWorkCompanyInfo();
        if (null == workCompanyInfo) {
            log.info("Function[dealWithWorkCompanyInfo] work company info is null of apply:{}", applyInfo.getApplyNo());
            return;
        }

        workCompanyInfo.setApplyNo(applyInfo.getApplyNo());
        workCompanyInfo.setPromoUserId(applyInfo.getPromoUserId());
        workCompanyInfo.setUpdateAt(dateThreadLocal.get());
        if (null == workCompanyInfo.getId()) {
            workCompanyInfo.setCreateAt(dateThreadLocal.get());
        }

        workCompanyInfoService.saveOrUpdate(workCompanyInfo);
    }


    /**
     * deal with person info
     *
     * @param info ApplyInfoEntity
     */
    private void dealWithPersonInfo(ApplyInfoEntity info) {
        PersonInfoEntity personInfo = info.getPersonInfo();
        personInfo.setApplyNo(info.getApplyNo());
        personInfo.setPromoUserId(info.getPromoUserId());

        personInfo.setUpdateAt(dateThreadLocal.get());
        if (null == personInfo.getCreateAt()) {
            personInfo.setCreateAt(dateThreadLocal.get());
        }

        personInfoService.saveOrUpdate(personInfo);
    }

    /**
     * 处理公司信息
     *
     * @param info ApplyInfoEntity
     */
    private void dealWithCompanyInfo(ApplyInfoEntity info) {
        CompanyInfoEntity companyInfo = info.getCompanyInfo();
        if (null == companyInfo) {
            log.info("Function[dealWithCompanyInfo] company info is null of applyInfo:{}", info.getApplyNo());
            return;
        }
        companyInfo.setApplyNo(info.getApplyNo());
        companyInfo.setPromoUserId(info.getPromoUserId());
        companyInfo.setUpdateAt(dateThreadLocal.get());
        if (null == companyInfo.getCreateAt()) {
            companyInfo.setCreateAt(dateThreadLocal.get());
        }

        companyInfoService.saveOrUpdate(companyInfo);
    }

    /**
     * 处理资产信息
     *
     * @param applyInfo ApplyInfoEntity
     */
    private void dealWithAssetsInfo(ApplyInfoEntity applyInfo) {
        // 处理车产
        AssetsCarInfoEntity assetsCarInfo = applyInfo.getAssetsCarInfo();
        if (null != assetsCarInfo) {
            assetsCarInfo.setPromoUserId(applyInfo.getPromoUserId());
            assetsCarInfo.setApplyNo(applyInfo.getApplyNo());
            assetsCarInfo.setUpdateAt(dateThreadLocal.get());
            if (null == assetsCarInfo.getCreateAt()) {
                assetsCarInfo.setCreateAt(dateThreadLocal.get());
            }
            assetsCarInfoService.saveOrUpdate(assetsCarInfo);

            log.info("Function[dealWithAssetsInfo] saveOrUpdate car asset:{}", applyInfo.getApplyNo());
        }

        // 处理信用卡
        AssetsCreditInfoEntity assetsCreditInfo = applyInfo.getAssetsCreditInfo();
        if (null != assetsCreditInfo) {
            assetsCreditInfo.setPromoUserId(applyInfo.getPromoUserId());
            assetsCreditInfo.setApplyNo(applyInfo.getApplyNo());
            assetsCreditInfo.setUpdateAt(dateThreadLocal.get());
            if (null == assetsCreditInfo.getCreateAt()) {
                assetsCreditInfo.setCreateAt(dateThreadLocal.get());
            }
            assetsCreditInfoService.saveOrUpdate(assetsCreditInfo);

            log.info("Function[dealWithAssetsInfo] saveOrUpdate credit asset:{}", applyInfo.getApplyNo());
        }


        // 处理房产
        AssetsHouseInfoEntity assetsHouseInfo = applyInfo.getAssetsHouseInfo();
        if (null != assetsHouseInfo) {
            assetsHouseInfo.setPromoUserId(applyInfo.getPromoUserId());
            assetsHouseInfo.setApplyNo(applyInfo.getApplyNo());
            assetsHouseInfo.setUpdateAt(dateThreadLocal.get());
            if (null == assetsHouseInfo.getCreateAt()) {
                assetsHouseInfo.setCreateAt(dateThreadLocal.get());
            }
            assetsHouseInfoService.saveOrUpdate(assetsHouseInfo);
            log.info("Function[dealWithAssetsInfo] saveOrUpdate house asset:{}", applyInfo.getApplyNo());
        }
    }


    private void dealWithTaxInfos(ApplyInfoEntity applyInfo) {
        List<TaxInfoEntity> taxInfoList = applyInfo.getTaxInfoList();
        if (ListUtil.isEmpty(taxInfoList)) {
            return;
        }

        for (TaxInfoEntity taxInfo : taxInfoList) {
            taxInfo.setPromoUserId(applyInfo.getPromoUserId());
            taxInfo.setApplyNo(applyInfo.getApplyNo());
            if (null == taxInfo.getId()) {
                taxInfo.setCreateAt(dateThreadLocal.get());
            }
            taxInfo.setUpdateAt(dateThreadLocal.get());
        }

        taxInfoService.saveOrUpdateBatch(taxInfoList);

    }

    private void dealWithLoanInfos(ApplyInfoEntity applyInfo) {
        List<LoanInfoEntity> loanInfoList = applyInfo.getLoanInfoList();
        if (ListUtil.isEmpty(loanInfoList)) {
            return;
        }

        //delete all the shit
        loanInfoService.update(buildUpdateDeleteStatusWrapper(applyInfo.getApplyNo()));


        // save or update
        for (LoanInfoEntity loanInfo : loanInfoList) {
            loanInfo.setIsDelete(DataValidConstant.IS_DELETE_FALSE_STR);
            loanInfo.setPromoUserId(applyInfo.getPromoUserId());
            loanInfo.setApplyNo(applyInfo.getApplyNo());
            if (null == loanInfo.getId()) {
                loanInfo.setCreateAt(dateThreadLocal.get());
            }
            loanInfo.setUpdateAt(dateThreadLocal.get());
        }

        loanInfoService.saveOrUpdateBatch(loanInfoList);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(Long id) {
        ApplyInfoEntity target = getById(id);
        if (null == target) {
            log.error("Function[delete] can't get:{} of applyInfo", id);
            return false;
        }

        // 更新is_delete状态
        boolean result = false;
        try {
            String applyNo = target.getApplyNo();

            assetsHouseInfoService.update(buildUpdateDeleteStatusWrapper(applyNo));
            assetsCarInfoService.update(buildUpdateDeleteStatusWrapper(applyNo));
            assetsCreditInfoService.update(buildUpdateDeleteStatusWrapper(applyNo));


            taxInfoService.update(buildUpdateDeleteStatusWrapper(applyNo));
            personInfoService.update(buildUpdateDeleteStatusWrapper(applyNo));
            companyInfoService.update(buildUpdateDeleteStatusWrapper(applyNo));


            result = update(buildUpdateDeleteStatusWrapper(applyNo));
        } catch (Exception e) {
            log.error("Function[delete],id:" + id, e);
        }
        log.info("Function[delete] soft delete applyInfo:{},result:{}", id, result);

        return result;
    }

    private <T> UpdateWrapper<T> buildUpdateDeleteStatusWrapper(String applyNo) {
        UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq(ApplyInfoEntity.APPLY_NO, applyNo);
        updateWrapper.set(ApplyInfoEntity.UPDATE_AT, new Date());
        updateWrapper.set(ApplyInfoEntity.IS_DELETE, DataValidConstant.IS_DELETE_TRUE_STR);

        return updateWrapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteBatch(Long[] ids) {
        for (Long id : ids) {
            delete(id);
        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePromoUser(Long operatorId, Long[] ids, Long promoUserId) {
        try {
            for (Long id : ids) {
                if (null == id) {
                    continue;
                }
                ApplyInfoEntity target = getById(id);
                String applyNo = target.getApplyNo();

                assetsHouseInfoService.update(buildUpdatePromoUserWrapper(applyNo, promoUserId));
                assetsCarInfoService.update(buildUpdatePromoUserWrapper(applyNo, promoUserId));
                assetsCreditInfoService.update(buildUpdatePromoUserWrapper(applyNo, promoUserId));


                taxInfoService.update(buildUpdatePromoUserWrapper(applyNo, promoUserId));
                personInfoService.update(buildUpdatePromoUserWrapper(applyNo, promoUserId));
                companyInfoService.update(buildUpdatePromoUserWrapper(applyNo, promoUserId));


                saveLog(applyNo, operatorId, target.getPromoUserId(), promoUserId);


                boolean update = update(buildUpdatePromoUserWrapper(applyNo, promoUserId));

                log.info("Function[updatePromoUser] update promoUser to:{} of applyInfo:{},result:{}", promoUserId, id, update);
            }
        } catch (Exception e) {
            log.error("Function[updatePromoUser] ids:" + JSON.toJSONString(ids), e);
            throw e;
        }
    }


    private void saveLog(String applyNo, Long operatorId, Long originPromoUserId, Long toPromoUserId) {
        Date now = new Date();

        LoanApplyReplaceUserLogEntity loanApplyReplaceUserLog = new LoanApplyReplaceUserLogEntity();
        loanApplyReplaceUserLog.setApplyNo(applyNo);
        loanApplyReplaceUserLog.setCreateAt(now);
        loanApplyReplaceUserLog.setCreateUserId(operatorId);
        loanApplyReplaceUserLog.setUpdateAt(now);
        loanApplyReplaceUserLog.setOriginPromoUserId(originPromoUserId);
        loanApplyReplaceUserLog.setToPromoUserId(toPromoUserId);
        loanApplyReplaceUserLog.setIsDelete(DataValidConstant.IS_DELETE_FALSE_STR);

        loanApplyReplaceUserLogService.add(loanApplyReplaceUserLog);
    }

    private <T> UpdateWrapper<T> buildUpdatePromoUserWrapper(String applyNo, Long promoUserId) {
        UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq(ApplyInfoEntity.APPLY_NO, applyNo);
        updateWrapper.set(ApplyInfoEntity.UPDATE_AT, new Date());
        updateWrapper.set(ApplyInfoEntity.PROMO_USER_ID, promoUserId);

        return updateWrapper;
    }

    private <T> QueryWrapper<T> buildApplyNoQueryWrapper(String applyNo) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("apply_no", applyNo);
        queryWrapper.eq("is_delete", DataValidConstant.IS_DELETE_FALSE_STR);

        return queryWrapper;
    }
}